package biblioes;

import game.model.Componente;
import game.model.Destino;
import game.model.EspejoDoble;
import game.model.EspejoSimple;
import game.model.Juego;
import game.model.Origen;
import game.model.Pared;
import game.model.SemiEspejo;
import game.model.Tablero;
import game.model.Trampa;
import game.model.Vacio;

import java.awt.Color;
import java.awt.Point;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
 * Clase que se encarga de la transferencia de informacion entre el juego
 * y archivos de almacenamiento, tanto de partidas guardadas, como de
 * niveles existentes.
 */

public class Archivos {
	
	/**
	 * Se encarga de retornar la proxima linea con informacion relevante.
	 * 
	 * @param archivo archivo del cual se van a levantar los valores.
	 * @return String la linea con la informacion.
	 * @throws IOException si hubiera error en la lectura.
	 */
	
	private static String lineaParseable(BufferedReader archivo) throws IOException {
		String linea = "#";
		do {
			if(!(linea.length() == 0) && linea.charAt(0) != '#') {
				break;
			}
			linea = archivo.readLine();
		} while(linea != null);
		return linea;
	}
	
	/**
	 * Se encarga de parsear la linea y dividirla en los valores.
	 * 
	 * @param linea string con los enteros a parsear.
	 * @return int[] array de enteros parseados de la linea recibida.
	 * @throws IOException si la linea no tuviera la cantidad esperada
	 * de argumentos.
	 * 
	 */
	
	private static int[] atoi(String linea) throws IOException {
		linea = linea.split("#")[0];
		String[] valores = linea.split(",");
		if(valores.length > 7) {
			throw new IOException();
		}
		int[] retorno = new int[valores.length];
		for(int cant = 0; cant < valores.length; cant++) {
			retorno[cant] = Integer.parseInt(valores[cant].replaceAll("\\s+", ""));
		}
		return retorno;
	}
	
	/**
	 * Se encarga de retornar una instancia de Componente, segun cual sea
	 * el entero que describe el componente en el archivo.
	 * Ademas es la responsable de validar que los datos sean validos.
	 * 
	 * @param datos array con los enteros levantados de la linea del archivo.
	 * @return Componente instancia de la clase Componente.
	 */
	
	private static Componente instanciaClase(int[] datos) {
		Componente retorno = null;
		switch(datos[2]) {
			case(1):	
						if(datos[4] <= 255 && datos[5] <= 255 && datos[6] <= 255) {
							if(datos[4] >= 0 && datos[5] >= 0 && datos[6] >= 0) {
								if(datos[3] >= 0 && datos[3] <= 3) {
									retorno = new Origen(new Color(datos[4], datos[5], datos[6]), datos[3]);
									break;
						}	}	}
						throw new IllegalArgumentException();
			case(2):	
						if(datos[4] <= 255 && datos[5] <= 255 && datos[6] <= 255) {
							if(datos[4] >= 0 && datos[5] >= 0 && datos[6] >= 0) {
								if(datos[3] == 0) {
									retorno = new Destino(new Color(datos[4], datos[5], datos[6]));
									break;
						}	}	}
						throw new IllegalArgumentException();
			case(3):	
						if(datos[4] == 0 && datos[5] == 0 && datos[6] == 0) {
							if(datos[3] >= 0 && datos[3] <= 3) {
								retorno = new EspejoSimple(datos[3]);
								break;
							}
						}
						throw new IllegalArgumentException();
			case(4): 
						if(datos[4] == 0 && datos[5] == 0 && datos[6] == 0) {
							if(datos[3] == 0 || datos[3] == 1) {
								retorno = new EspejoDoble(datos[3]);
								break;
							}
						}
						throw new IllegalArgumentException();
			case(5): 
						if(datos[4] == 0 && datos[5] == 0 && datos[6] == 0) {
							if(datos[3] == 0 || datos[3] == 1) {
								retorno = new SemiEspejo(datos[3]); 
								break;
							}
						}
						throw new IllegalArgumentException();
			case(6): 
						if(datos[4] == 0 && datos[5] == 0 && datos[6] == 0) {
							if(datos[3] == 0) {
								retorno = new Pared();
								break;
							}
						}
						throw new IllegalArgumentException();
			case(7): 
						if(datos[4] == 0 && datos[5] == 0 && datos[6] == 0) {
							if(datos[3] == 0) {
								retorno = new Trampa();
								break;
							}
						}
						throw new IllegalArgumentException();
			default: 
						throw new IllegalArgumentException();
		}
		return retorno;
	}
	
	/**
	 * Se encarga de cargar el juego con la informacion que levanta del archivo.
	 * 
	 * @param juegoTemporal parametro de entrada salida en el cual se guardara la informacion
	 * obtenida del archivo.
	 * @param myFile archivo con la informacion a levantar.
	 * @throws IOException si hubiera error en la lectura.
	 */
	
	public static void cargarArchivo(Juego juegoTemporal, File myFile) throws IOException {
		BufferedReader file = new BufferedReader(new FileReader(myFile));
		
		String linea = null;
		int[] valores = null;
		
		try {
			if(juegoTemporal.verJugador() == null) {
				linea = lineaParseable(file);
				juegoTemporal.cambiaJugador(linea);
			}
			linea = lineaParseable(file);
			juegoTemporal.cambiaNivel(linea);
			linea = lineaParseable(file);
			valores = atoi(linea);
			juegoTemporal.cambiaTablero(new Tablero(valores[0], valores[1]));
			while((linea = lineaParseable(file)) != null) {
				valores = atoi(linea);
				if(juegoTemporal.getTablero().getCelda(new Point(valores[0], valores[1])) != null) {
					throw new IllegalArgumentException();
				}
				juegoTemporal.getTablero().setCelda(new Point(valores[0], valores[1]), instanciaClase(valores));
			}
			for(int i = 0; i < juegoTemporal.getTablero().getDimensiones().x; i++) {
				for(int j = 0; j < juegoTemporal.getTablero().getDimensiones().y; j++) {
					if(juegoTemporal.getTablero().getCelda(new Point(i, j)) == null) {
						juegoTemporal.getTablero().setCelda(new Point(i ,j), new Vacio());
					}
				}
			}
		} catch(IllegalArgumentException e) {
			throw e;
		}
		finally {
			if(file != null) {
				file.close();
			}
		}
	}

	/**
	 * Se encarga de guardar en un archivo el juego que se esta ejecutando.
	 * 
	 * @param juegoTemporal parametro de entrada del cual se obtendra la informacion
	 * que sera salvada en el archivo.
	 * @param archivo archivo en el que se guardara la informacion.
	 * @throws IOException si hubiera error en la escritura.
	 */
	
	public static void guardarArchivo(Juego juegoTemporal, File archivo) throws IOException {
		if(archivo.exists() == false) {
			archivo.createNewFile();
		}
		BufferedWriter file = new BufferedWriter(new FileWriter(archivo));
		
		Tablero salvar = juegoTemporal.getTablero();
		int filas = salvar.getDimensiones().x;
		int columnas = salvar.getDimensiones().y;
		
		String linea = null;
		
		file.write(juegoTemporal.verJugador());
		file.newLine();
		file.newLine();
		file.write(juegoTemporal.verNivel());
		file.newLine();
		file.newLine();
		file.write(filas + "," + columnas);
		file.newLine();
		file.newLine();
		
		try {
			for(int i = 0; i < filas; i++) {
				for(int j = 0; j < columnas; j++) {
					Point posicion = new Point(i,j);
					linea = i + ","+ j + ",";
					linea = salvar.getCelda(posicion).getComponente().salvarEstado(linea);
					file.write(linea);
					file.newLine();
				}
			}
		} finally {
			if(file != null) {
				file.close();
			}
		}
	}
	
}
